fix(clade): name every code, and stop the TSV reader eating empty fields - #50
Merged
Conversation
Two problems, both in how a CLADE.a2ml gets written.
── 1. A code cannot express a wrong belief, so it cannot be checked for one ──
CLADE-003 checks the primary code is one of the 12. It cannot check the author
meant that clade. Every code is valid whatever you thought it stood for, so these
passed every gate:
paint-type primary = "pt" believing "PainT-type"
pt is Protocols & Interop. paint-type is an image editor (ap).
gossamer primary = "gv" believing "Graphical/Visual"
gv is GoVernance & Standards. There is no graphical clade (dx).
The two letters abbreviate the CLADE's name. They never abbreviate the REPO's
name. This is a closed taxonomy of categories, not an abbreviation scheme — but
nothing said so at the point of authoring, and nothing could catch it.
Fix: every CLADE.a2ml now declares the name beside the code —
[clade]
primary = "ap"
primary-name = "Applications"
— and CLADE-006 (BLOCK, pre-commit) rejects a pair that disagrees with
verisim/seed/clades.a2ml. The redundancy is the point: the pair CAN express the
wrong belief, so the pair can be rejected. Verified against all 10 cases,
including the two real historical errors:
ap|Applications PASS
dx|Developer Ecosystem PASS
gv|Governance & Standards PASS (ampersand survives)
pt|PainT-type FAIL <- the actual paint-type error
gv|Graphical/Visual FAIL <- the actual gossamer error
pt|Applications FAIL valid code, wrong name
ap|Protocols & Interop FAIL valid name, wrong code
zz|Applications FAIL invalid code
ap|<missing> FAIL cannot omit the name
UNASSIGNED|UNASSIGNED FAIL template default fails loudly
clades.a2ml and the spec now state the rule and name both real errors, so the
next author reads why before choosing. deploy-clade-a2ml.sh emits primary-name,
and refuses to write an identity it cannot name.
── 2. The TSV reader collapsed empty fields — 153 files already corrupted ──
TAB IS IFS WHITESPACE, so `IFS=$'\t' read` treats a run of tabs as ONE delimiter:
empty fields vanish and everything after them shifts left. 316 of the 319 seed
entries have an empty `parent`, so this was the normal case, not an edge case:
proven <tab> fv <tab> [] <tab> standalone <tab><tab> Formally verified… <tab> hyperpolymath
^^ empty parent collapses
-> parent="Formally verified…" description="hyperpolymath" owner=""
The damage is real and committed — 153 CLADE.a2ml files across the estate carry
`rationale = ""` with the repo's DESCRIPTION sitting in `parent`:
panic-attack parent = "Security scanning tool — static analysis…"
flat-mate parent = "Flat/apartment sharing management…"
rpa-elysium parent = "Robotic Process Automation framework…"
live-files parent = "Live file synchronisation tool…"
This also silently defeated the `owner` support added in #48: $7 was always empty,
so owner always fell back to "hyperpolymath" and an explicit
`owner = "metadatastician"` in the seed was ignored. #48's own regression test
did not catch it because export-json.sh uses `awk -F'\t'`, which does NOT
collapse — the two derivation sites disagreed, and only the tested one was right.
Fix: translate the tabs to US (0x1f) before reading. 0x1f is not IFS whitespace,
so runs are not collapsed; it cannot occur in the data (0 occurrences in
repos.a2ml); it is exactly what the ASCII unit separator is for. The parser's TSV
contract is unchanged.
Verified end-to-end on a scratch tree:
proven (empty parent, the 316-entry case)
rationale = "Formally verified safety library — Idris2 dependent types…" (was "")
parent = "" (was the description)
uuid = ffa8e1f5-… (unchanged, matches registry)
cadastra (owner = "metadatastician" in seed)
uuid = da97ea76-1715-5331-963b-27f011b10d22 (correct — #48 works for the first time)
primary-owner = "metadatastician"
Also verified: repos.json regenerates BYTE-IDENTICAL (no uuid moves), both a2ml
files still parse as TOML (the dogfood gate uses tomllib), and the spec renders.
Repairing the 153 already-corrupted files is a separate, owner-facing job — this
only stops the script producing more.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
hyperpolymath
marked this pull request as ready for review
July 17, 2026 04:11
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Two problems, both in how a
CLADE.a2mlgets written. The second one defeats my own merged #48 — details below.1. A code cannot express a wrong belief, so it cannot be checked for one
CLADE-003 checks the code is one of the 12. It cannot check the author meant that clade — every code is valid whatever you thought it stood for. So these passed every gate:
pt/gvactually meanprimary = "pt"ap)primary = "gv"dx)The two letters abbreviate the CLADE's name. They never abbreviate the REPO's name. It is a closed taxonomy of categories, not an abbreviation scheme — but nothing said so at the point of authoring, and nothing could catch it.
Fix: write the name beside the code, and check the pair
CLADE-006 (BLOCK, pre-commit) rejects any pair disagreeing with
verisim/seed/clades.a2ml. The redundancy is the point — the pair can express the wrong belief, so the pair can be rejected. Verified on all 10 cases, including both real historical errors:clades.a2mland the spec now state the rule and name both real errors, so the next author reads why before choosing.deploy-clade-a2ml.shemitsprimary-nameand refuses to write an identity it cannot name.2. The TSV reader collapsed empty fields — 153 files already corrupted
Tab is IFS whitespace, so
IFS=$'\t' readtreats a run of tabs as one delimiter: empty fields vanish and everything after shifts left. 316 of 319 seed entries have an emptyparent— this was the normal case, not an edge case:The damage is real and committed — 153 CLADE.a2ml files carry
rationale = ""with the repo's description sitting inparent:This defeated #48, and #48's own test could not see it
The
ownersupport I added in #48 never worked here:$7was always empty, soowneralways fell back tohyperpolymathand an explicitowner = "metadatastician"was ignored. #48's byte-identical regression test passed becauseexport-json.shusesawk -F'\t', which does not collapse — the two derivation sites disagreed and only the tested one was correct. My apologies: I shipped that.Fix
Translate tabs to US (
0x1f) before reading. It is not IFS whitespace so runs are not collapsed, it cannot occur in the data (0 occurrences inrepos.a2ml), and it is exactly what the ASCII unit separator is for. The parser's TSV contract is unchanged.Verification
Scratch tree, end to end:
Also:
repos.jsonregenerates byte-identical (no uuid moves), both a2ml files still parse as TOML (the dogfood gate usestomllib), and the spec renders under asciidoctor.Not in this PR
Repairing the 153 already-corrupted files is a separate, owner-facing job — this only stops the script producing more. Related: #49.
Draft: this changes the clade contract for every repo, so it is yours to review.
🤖 Generated with Claude Code